require("curl")
require("lsqlite3")
require("xml")

local tonumber = tonumber
local assert = assert

local sqlite3_OK = sqlite3.OK
local sqlite3_ROW = sqlite3.ROW
local sqlite3_DONE = sqlite3.DONE

local DO_NOT_GET = true

local exec_db_statements = function(db, ...)
	for i = 1, select("#", ...) do
		local statement = select(i, ...)
		local result = db:exec(statement)
		if result ~= sqlite3_OK then
			print("SQL Statement failed:")
			print(statement)
			print(db:errmsg())
		end
	end
end

local create_db = function (dbname, sqlfile)
	pcall(os.remove, dbname)
	local db = sqlite3.open(dbname)
	if sqlfile then
		local f = io.open(sqlfile, "r")
		local data = f:read("*a")
		f:close()
		exec_db_statements(db, strsplit(";", data))
	end
	return db
end

local cache_db = sqlite3.open("armory.db")
local items_db = create_db("items.db", "make_items_db.sql")

local function get(url)
	local temp = {}
	local c = curl.easy_init()
	print("GET", url) io.flush()
	c:setopt(curl.OPT_URL, url)
	c:setopt(curl.OPT_USERAGENT, "Firefox/2.0")
	c:setopt(curl.OPT_WRITEFUNCTION, function (s, len) temp[#temp + 1] = s ; return len end)
	local err, info = c:perform()
	if err ~= 0 then
		print("Error : "..info)
		return
	else
		temp = table.concat(temp)
		if temp:len() > 0 then
			return temp
		end
	end
end

local statements = setmetatable({}, {__index = function (table, stmt)
	local t = items_db:prepare(stmt)
	table[stmt] = t
	return t
end})

local function apply_stmt(s, ...)
	local stmt = s
	if type(s) == "string" then
		stmt = statements[s]
	end
	stmt:bind_values(...)
	if stmt:step() ~= sqlite3_DONE then
		local msg = string.format("Error in apply_stmt(%q) : %s", s, items_db:errmsg())
		error(msg, 2)
	end
	stmt:reset()
end

local cached_sdb
do
	
	local make_db = function (table)
		local select_stmt = items_db:prepare(string.format("SELECT id FROM %s WHERE name = ?", table))
		local insert_stmt = items_db:prepare(string.format("INSERT INTO %s (name) VALUES (?)", table))
		return setmetatable({}, {
			__index = function (table, column)
				select_stmt:bind_values(column)
				local r = select_stmt:step()
				if r == sqlite3_ROW then
					r = select_stmt:get_value(0)
				else
					apply_stmt(insert_stmt, column)
					r = items_db:last_insert_rowid()
				end
				select_stmt:reset()
				r = tonumber(r)
				table[column] = r
				return r
			end,
		})
	end
	
	cached_sdb = setmetatable({}, {
		__index = function (table, column)
			local r = make_db (column)
			table[column] = r
			return r
		end,
	})
end

local getiteminfo, getitemtooltip
do
	local select_stmt = cache_db:prepare("SELECT content FROM cache WHERE lang = ? AND type = ? AND id = ?")
	
	local function cached_get(type, id, l)
		select_stmt:bind_values(l, type, id)
		local result = select_stmt:step()
		if result == sqlite3_ROW then
			result = select_stmt:get_value(0)
			select_stmt:reset()
			return result
		else
			select_stmt:reset()
		end
	end

	getiteminfo = function (id)
		return cached_get("info", id, 'EN') or cached_get("info", id, 'US')
	end

	getitemtooltip = function (id)
		return cached_get("tooltip", id, 'EN') or cached_get("tooltip", id, 'US')
	end
end

local function get_item_cost(info)
	local cost = info:get_first_child("cost")
	if not cost then return end
	return tonumber(cost:get_attribute("sellPrice"))
end

local subclass_db = cached_sdb["itemSubclasses"]
local bound_db = cached_sdb["boundNameRefs"]
local bonus_db = cached_sdb["bonusTypes"]
local area_db = cached_sdb["areaNames"]

--[[
local bonus_patterns = {
	["^Increases healing done by spells and effects by up to (%d+)%.$"] = "Healing Bonus",
	["^Increases damage and healing done by magical spells and effects by up to (%d+)%.$"] = "Healing and Damage Bonus",
	["^Restores (%d+) health over (%d+) sec%.  Must remain seated while eating%."] = function (a, b) return "Food heal", a end,
	["^Restores (%d+) mana over (%d+) sec%.  Must remain seated while drinking%."] = function (a, b) return "Drink mana", a end,
	["^Restores (%d+) to (%d+) health%.$"] = function (a, b) return "Give Health", (a + b) / 2 end,
	["^Restores (%d+) health every 5 sec%.$"] = "Health regen",
	["^Increase you critical strike rating by (%d+)%.$"] = "Crit Rating",
	["^Increases attack power by (%d+)%.$"] = "Attack Power",
	["^Increases defense rating by (%d+)%.$"] = "Defense Skill Rating",
	["^Increases damage done by (%a+) spells and effects by up to (%d+)%.$"] = function (type, v) return type.." Damage Bonus", v end,
	["^Increases the target's (%a+) by (%d+) for 30 min%.$"] = function (type, v) return type.." Buff", v end,
}

local function handle_bonus_pattern(info, ...)
	local match = (...)
	if not match then return end
	local t_info = type(info)
	if t_info == "string" then
		return info, match
	elseif t_info == "function" then
		return info(...)
	end
end

local function decompose_bonus(b)
	for pattern, info in pairs(bonus_patterns) do
		local bonus, value = handle_bonus_pattern(info, pattern:match(b))
		if bonus then
			return bonus, value
		end
	end
	return b
end
]]

local function decompose_bonus(b)
	return b
end

local get_bonus_instance_id
do
	local select_bonus_instance_stmt
	
	local bonus_cache = {}
	
	get_bonus_instance_id = function (bonus, value)
		local bonus_string_id = bonus .. "\0" .. (value or "*nil*")
		local id = bonus_cache[bonus_string_id]
		if id then return id end
		
		local bonus_id = bonus_db[bonus]
		if not select_bonus_instance_stmt then
			select_bonus_instance_stmt = items_db:prepare("SELECT id FROM bonusInstances WHERE bonus_id = ? AND value = ?")
		end
		select_bonus_instance_stmt:bind_values(bonus_id, value)
		local res = select_bonus_instance_stmt:step()
		if res == sqlite3_ROW then
			res = select_bonus_instance_stmt:get_value(0)
		else
			apply_stmt("INSERT INTO bonusInstances (bonus_id, value) VALUES (?, ?)", bonus_id, value)
			res = items_db:last_insert_rowid()
		end
		select_bonus_instance_stmt:reset()
		id = tonumber(res)
		bonus_cache[bonus_string_id] = id
		return id
	end
end

local function get_item_type(tooltip)
	local data = tooltip:get_first_child("equipData")
	if not data then return end
	local invType = data:get_first_child("inventoryType")
	if invType then
		invType = tonumber(invType:get_content())
	end
	local invSubClass = data:get_first_child("subclassName")
	if invSubClass then
		invSubClass = subclass_db[invSubClass:get_content()]
	end
	return invType, invSubClass
end

local function get_item_maxcount(tooltip)
	local data = tooltip:get_first_child("maxCount")
	if not data then return end
	return tonumber(data:get_content())
end

local function get_item_bind_id(v)
	if not v then return end
	return bound_db[v:get_content()]
end

local function get_item_damage_info(info, item_id)
	if not info then return end
	local speed = info:get_first_child("speed")
	if speed then
		speed = tonumber(speed:get_content())
	end
	local data = {}
	for child in info:iterate_children("damage") do
		local type = tonumber(child:get_first_child("type"):get_content())
		local min = tonumber(child:get_first_child("min"):get_content())
		local max = tonumber(child:get_first_child("max"):get_content())
		apply_stmt("INSERT INTO itemDamages VALUES (?, ?, ?, ?)", item_id, type, min, max)
	end
	
	return speed
end

local function get_item_durability(info)
	if not info then return end
	return tonumber(info:get_attribute("max"))
end

local get_item_socketbonus
do
	local type_db = cached_sdb["slotTypes"]
	function get_item_socketbonus(data, item_id)
		if not data then return end
		local slot = 1
		for child in data:iterate_children("socket") do
			local color = child:get_attribute("color")
			local type_id = type_db[color]
			
			apply_stmt("INSERT INTO itemSlots VALUES (?, ?, ?)", item_id, slot, type_id)
			slot = slot + 1
		end
		return get_bonus_instance_id(decompose_bonus(data:get_first_child("socketMatchEnchant"):get_content()))
	end
end

local handle_item_set_info
do
	local set_db = cached_sdb["itemSets"]
	
	handle_item_set_info = function (data, item_id)
		if not data then return end
		local set_name = data:get_first_child("name"):get_content()
		local has_set = not not rawget(set_db, set_name)
		local set_id = set_db[set_name]
		apply_stmt("INSERT INTO itemSetItems VALUES (?, ?)", set_id, item_id)
		if not has_set then
			-- add bonuses
			local bonus = 1
			for child in data:iterate_children("setBonus") do
				local bonus_id = get_bonus_instance_id(decompose_bonus(child:get_attribute("desc")))
				local threshold = child:get_attribute("threshold")
				apply_stmt("INSERT INTO itemSetBonuses VALUES (?, ?, ?, ?)", set_id, bonus, threshold, bonus_id)
				bonus = bonus + 1
			end
		end
	end
end

local get_item_requirements
do
	local ability_db = cached_sdb["abilityNames"]
	local skill_db = cached_sdb["skillNames"]
	local class_db = cached_sdb["classNames"]
	local race_db = cached_sdb["raceNames"]
	local faction_db = cached_sdb["factionNames"]
	
	get_item_requirements = function (tooltip, item_id)
		local data
		-- classes
		data = tooltip:get_first_child("allowableClasses")
		if data then
			for child in data:iterate_children("class") do
				local class_id = class_db[child:get_content()]
				apply_stmt("INSERT INTO itemClassReqs VALUES (?, ?)", item_id, class_id)
			end
		end
		-- races
		data = tooltip:get_first_child("allowableRaces")
		if data then
			for child in data:iterate_children("race") do
				local race_id = race_db[child:get_content()]
				apply_stmt("INSERT INTO itemRaceReqs VALUES (?, ?)", item_id, race_id)
			end
		end
		-- ability
		data = tooltip:get_first_child("requiredAbility")
		if data then
			local ability_id = ability_db[data:get_content()]
			apply_stmt("INSERT INTO itemAbilityReqs VALUES (?, ?)", item_id, ability_id)
		end
		-- skill
		data = tooltip:get_first_child("requiredSkill")
		if data then
			local skill_id = skill_db[data:get_attribute("name")]
			local rank = tonumber(data:get_attribute("rank"))
			apply_stmt("INSERT INTO itemSkillReqs VALUES (?, ?, ?)", item_id, skill_id, rank)
		end
		-- faction
		data = tooltip:get_first_child("requiredFaction")
		if data then
			local faction_id = faction_db[data:get_attribute("name")]
			local standing_id = tonumber(data:get_attribute("rep"))
			apply_stmt("INSERT INTO itemFactionReqs VALUES (?, ?, ?)", item_id, faction_id, standing_id)
		end
		-- return level
		data = tooltip:get_first_child("requiredLevel")
		if data then
			return tonumber(data:get_content())
		end
	end
end

local handle_bonus_info
do
	local trigger_db = cached_sdb["triggerNames"]
	local suffix_db = cached_sdb["randomPropertiesSuffixes"]
	local function insert_bonus(item_id, bonus, value)
		local bonus_id = get_bonus_instance_id(bonus, value)
		apply_stmt("INSERT INTO itemBonuses VALUES (?, ?)", item_id, bonus_id)
	end
	local baseInfos = {
		["armor"] = "Armor",
		["blockValue"] = "Block Value",
		["bonusStrength"] = "Strength",
		["bonusAgility"] = "Agility",
		["bonusStamina"] = "Stamina",
		["bonusIntellect"] = "Intellect",
		["bonusSpirit"] = "Spirit",
		["fireResist"] = "Fire Resistance",
		["natureResist"] = "Nature Resistance",
		["frostResist"] = "Frost Resistance",
		["shadowResist"] = "Shadow Resistance",
		["arcaneResist"] = "Arcane Resistance",
		["bonusDefenseSkillRating"] = "Defense Skill Rating",
		["bonusDodgeRating"] = "Dodge Rating",
		["bonusParryRating"] = "Parry Rating",
		["bonusBlockRating"] = "Block Rating",
		["bonusHitMeleeRating"] = "Hit Melee Rating",
		["bonusHitRangedRating"] = "Hit Ranged Rating",
		["bonusHitSpellRating"] = "Hit Spell Rating",
		["bonusCritMeleeRating"] = "Crit Melee Rating",
		["bonusCritRangedRating"] = "Crit Ranged Rating",
		["bonusCritSpellRating"] = "Crit Spell Rating",
		["bonusHitTakenMeleeRating"] = "Hit Taken Melee Rating",
		["bonusHitTakenRangedRating"] = "Hit Taken Ranged Rating",
		["bonusHitTakenSpellRating"] = "Hit Taken Spell Rating",
		["bonusCritTakenMeleeRating"] = "Crit Taken Melee Rating",
		["bonusCritTakenRangedRating"] = "Crit Taken Ranged Rating",
		["bonusCritTakenSpellRating"] = "Crit Taken Spell Rating",
		["bonusHasteMeleeRating"] = "Haste Melee Rating",
		["bonusHasteRangedRating"] = "Haste Ranged Rating",
		["bonusHasteSpellRating"] = "Haste Spell Rating",
		["bonusHitRating"] = "Hit Rating",
		["bonusCritRating"] = "Crit Rating",
		["bonusHitTakenRating"] = "Hit Taken Rating",
		["bonusCritTakenRating"] = "Crit Taken Rating",
		["bonusResilienceRating"] = "Resilience Rating",
		["bonusHasteRating"] = "Haste Rating",
	}
	handle_bonus_info = function (tooltip, info, item_id)
		local data

		for data in tooltip:iterate_children() do
			local childname = data.label
			local bonus = baseInfos[childname]
			if bonus then
				insert_bonus(item_id, bonus, tonumber(data:get_content()))
			end
		end
		
		local spells = tooltip:get_first_child("spellData")
		if spells then
			local index = 1
			for data in spells:iterate_children("spell") do
				local trigger_id = tonumber(data:get_first_child("trigger"):get_content())
				local bonus_id = get_bonus_instance_id(decompose_bonus(data:get_first_child("desc"):get_content()))
				apply_stmt("INSERT INTO itemSpells VALUES (?, ?, ?, ?)", item_id, index, trigger_id, bonus_id)
				index = index + 1
			end
		end
		local data = info:get_first_child("randomProperties")
		if data then
			for prop in data:iterate_children("randomProperty") do
				local suffix_id = suffix_db[prop:get_attribute("suffix")]
				apply_stmt("INSERT INTO randomPropertiesInstances VALUES (NULL, ?, ?)", item_id, suffix_id)
				local prop_id = items_db:last_insert_rowid()
				for enchant in prop:iterate_children("randomPropertyEnchant") do
					local name = enchant:get_attribute("name")
					local bonus_id
					local amount, type = name:match("^%+(%d+) (.*)$")
					if amount and type then
						bonus_id = get_bonus_instance_id(type, amount)
					else
						bonus_id = get_bonus_instance_id(decompose_bonus(name))
					end
					apply_stmt("INSERT INTO randomProperties VALUES (?, ?)", prop_id, bonus_id)
				end
			end
		end
	end
end

local handle_quest_info
do
	local quest_cache = {}
	
	local function get_quest_id (qinfo)
		local id = tonumber(qinfo:get_attribute("id"))
		local name = qinfo:get_attribute("name")
		local area_id = area_db[qinfo:get_attribute("area")]
		local level = tonumber(qinfo:get_attribute("level"))
		local reqMinLevel = tonumber(qinfo:get_attribute("reqMinLevel"))
		local suggestedPartySize = tonumber(qinfo:get_attribute("suggestedPartySize"))
		
		if not quest_cache[id] then
			apply_stmt("INSERT INTO quests VALUES (?, ?, ?, ?, ?, ?)", id, name, area_id, level, reqMinLevel, suggestedPartySize)
			quest_cache[id] = name
		else
			assert(name == quest_cache[id])
		end
		return id
	end
	
	local quest_infos = {
		rewardFromQuests = "Reward",
		objectiveOfQuests = "Objective",
		startsQuest = "Starts",
		providedForQuests = "Provided",
	}
	
	local relation_db = cached_sdb["relationNames"]
	handle_quest_info = function (info, item_id)
		for childname, relation_name in pairs(quest_infos) do
			local data = info:get_first_child(childname)
			if data then
				local relation_id = relation_db[relation_name]
				for quest in data:iterate_children("quest") do
					local quest_id = get_quest_id(quest)
					apply_stmt("INSERT INTO questRelations VALUES (?, ?, ?)", item_id, quest_id, relation_id)
				end
			end
		end
	end
end

local handle_creature_info
do
	local types = {
		["dropCreatures"] = "Loot",
		["pickPocketCreatures"] = "PickPocket",
		["skinCreatures"] = "Skin",
		["herbCreatures"] = "Herb",
		["mineCreatures"] = "Mine",
	}

	local droptype_db = cached_sdb["dropTypes"]
	local creature_type_db = cached_sdb["creatureTypes"]
	
	local creature_cache = {}
	
	local function get_creature_id (cinfo)
		local id = tonumber(cinfo:get_attribute("id"))
		local name = cinfo:get_attribute("name")
		local title = cinfo:get_attribute("title")
		local area_id = area_db[cinfo:get_attribute("area")]
		local classification_id = tonumber(cinfo:get_attribute("classification"))
		local minLevel = tonumber(cinfo:get_attribute("minLevel"))
		local maxLevel = tonumber(cinfo:get_attribute("maxLevel"))
		local type_id = creature_type_db[cinfo:get_attribute("type")]
		local heroic = cinfo:get_attribute("heroic") and 1 or 0
		
		if not creature_cache[id] then
			apply_stmt("INSERT INTO creatures VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", id, name, title, area_id, classification_id, minLevel, maxLevel, type_id, heroic)
			creature_cache[id] = name
		else
			assert(creature_cache[id] == name)
		end
		return id
	end
	
	local function handle_drop_type_info (data, item_id, drop_id)
		for type in data:iterate_children("type") do
			local type_id = creature_type_db[type:get_attribute("name")]
			for area in data:iterate_children("area") do
				local area_id = area_db[area:get_attribute("name")]
				apply_stmt("INSERT INTO creatureTypeDrops VALUES (?, ?, ?, ?)", item_id, type_id, area_id, drop_id)
			end
		end
	end
	
	local function handle_drop_creature_info (data, item_id, drop_id)
		for cinfo in data:iterate_children("creature") do
			local creature_id = get_creature_id(cinfo)
			local dropRate = tonumber(cinfo:get_attribute("dropRate"))
			apply_stmt("INSERT INTO creatureDrops VALUES (?, ?, ?, ?)", item_id, creature_id, drop_id, dropRate)
		end
	end
	
	handle_creature_info = function (info, item_id)
	
		for data in info:iterate_children() do
			local drop = types[data.label]
			if drop then
				local drop_id = droptype_db[drop]
				handle_drop_creature_info(data, item_id, drop_id)
				data = data:get_first_child("info")
				if data then
					handle_drop_type_info(data, item_id, drop_id)
				end
			end
		end
	
		-- vendors are treated seperately
		data = info:get_first_child("vendors")
		if data then
			for cinfo in data:iterate_children("creature") do
				local creature_id = get_creature_id(cinfo)
				apply_stmt("INSERT INTO itemVendors VALUES (?, ?)", item_id, creature_id)
			end
		end
	end
end

local function handle_disenchant_info(info, item_id)
	local data = info:get_first_child("disenchantLoot")
	if data then
		local level = tonumber(data:get_attribute("requiredSkillRank"))
		apply_stmt("INSERT INTO itemDisenchantLevel VALUES (?, ?)", item_id, level)
		for item in data:iterate_children("item") do
			local result_id = tonumber(item:get_attribute("id"))
			local minCount = tonumber(item:get_attribute("minCount"))
			local maxCount = tonumber(item:get_attribute("maxCount"))
			local dropRate = tonumber(item:get_attribute("dropRate"))
			apply_stmt("INSERT INTO itemDisenchantOutcome VALUES (?, ?, ?, ?, ?)", item_id, result_id, minCount, maxCount, dropRate)
		end
	end
end

local handle_craft_info
do
	local craft_spell_table = {}
	
	local function add_craft_spell(data, plan_id)
		if not data then return end
		for spell in data:iterate_children("spell") do
			local spell_id = tonumber(spell:get_attribute("id"))
			local spell_name = spell:get_attribute("name")
			if craft_spell_table[spell_id] then
				assert(spell_name == craft_spell_table[spell_id])
				return
			else
				craft_spell_table[spell_id] = spell_name
			end
			local spell_icon = spell:get_attribute("icon")
			apply_stmt("INSERT INTO craftSpells VALUES (?, ?, ?, NULL)", spell_id, spell_name, spell_icon)
			for item in spell:iterate_children("item") do
				local item_id = tonumber(item:get_attribute("id"))
				local count = tonumber(item:get_attribute("count"))
				apply_stmt("INSERT INTO craftSpellItems VALUES (?, ?, 2, ?)", spell_id, item_id, count)
			end
			for item in spell:iterate_children("reagent") do
				local item_id = tonumber(item:get_attribute("id"))
				local count = tonumber(item:get_attribute("count"))
				apply_stmt("INSERT INTO craftSpellItems VALUES (?, ?, 1, ?)", spell_id, item_id, count)
			end
			if plan_id then
				apply_stmt("UPDATE craftSpells SET plan_id = ? WHERE id = ?", plan_id, spell_id)
			end
		end
	end
	
	handle_craft_info = function (info, item_id)
		add_craft_spell(info:get_first_child("reagentFor"))
		add_craft_spell(info:get_first_child("craftedBy"))
		add_craft_spell(info:get_first_child("plansFor"), item_id)
	end
end

local function handle_currency_info(info, item_id)
--[[
	local data = info:get_first_child("currencyFor")
	if data then
		for item in data:iterate_children("item") do
			local id = tonumber(item:get_attribute("id"))
			local cost = tonumber(item:get_first_child("cost"):get_first_child("token"):get_attribute("count"))
			apply_stmt("INSERT INTO itemCosts VALUES (?, ?, ?)", id, item_id, cost)
		end
	end
]]
	local data = info:get_first_child("cost")
	if data then
		for item in data:iterate_children("token") do
			local currency_id = tonumber(item:get_attribute("id"))
			local cost = tonumber(item:get_attribute("count"))
			apply_stmt("INSERT INTO itemCosts VALUES (?, ?, ?)", item_id, currency_id, cost)
		end
	end
end

local handle_container_info
do
	local object_cache = {}
	
	local function get_object_id (info)
		local id = tonumber(info:get_attribute("id"))
		local name = info:get_attribute("name")
		local area_id = area_db[info:get_attribute("area")]
		local heroic = info:get_attribute("heroic") and 1 or 0
		
		if not object_cache[id] then
			apply_stmt("INSERT INTO objects VALUES (?, ?, ?, ?)", id, name, area_id, heroic)
			object_cache[id] = name
		else
			assert(object_cache[id] == name)
		end
		return id
	end
	
	handle_container_info = function (info, item_id)
		local data = info:get_first_child("containerObjects")
		if data then
			for object in data:iterate_children("object") do
				local object_id = get_object_id(object)
				local dropRate = tonumber(object:get_attribute("dropRate"))
				apply_stmt("INSERT INTO objectDrops VALUES (?, ?, ?)", item_id, object_id, dropRate)
			end
		end
	end
end

local profile, report
do
	local profiled = {}
	local clock = os.clock

	function profile(name, value)
		local info = profiled[value]
		if not info then 
		
			info = {
				original = value,
				name = name,
				count = 0,
				time = 0,
			}
			local returner = function (start, ...)
				local stop = clock()
				info.count = info.count + 1
				info.time = info.time + stop - start
				return ...
			end
			local caller = function (...)
				local start = clock()
				return returner(start, value(...))
			end
			info.handler = caller
			
			profiled[value] = info
			profiled[caller] = info
		end
		return info.handler
	end
	
	function report()
		for k, v in pairs(profiled) do
			if k == v.original then
				print(v.name, v.count, v.time, v.time / v.count * 1000)
			end
		end
	end
end

if arg[1] == '-profile' then
	get_item_cost = profile("get_item_cost", get_item_cost)
	get_item_type = profile("get_item_type", get_item_type)
	get_item_maxcount = profile("get_item_maxcount", get_item_maxcount)
	get_item_bind_id = profile("get_item_bind_id", get_item_bind_id)
	get_item_damage_info = profile("get_item_damage_info", get_item_damage_info)
	get_item_durability = profile("get_item_durability", get_item_durability)
	get_item_socketbonus = profile("get_item_socketbonus", get_item_socketbonus)
	get_item_requirements = profile("get_item_requirements", get_item_requirements)
	handle_item_set_info = profile("handle_item_set_info", handle_item_set_info)
	handle_bonus_info = profile("handle_bonus_info", handle_bonus_info)
	handle_quest_info = profile("handle_quest_info", handle_quest_info)
	handle_creature_info = profile("handle_creature_info", handle_creature_info)
	handle_container_info = profile("handle_container_info", handle_container_info)
	handle_disenchant_info = profile("handle_disenchant_info", handle_disenchant_info)
	handle_craft_info = profile("handle_craft_info", handle_craft_info)
	handle_currency_info = profile("handle_currency_info", handle_currency_info)
	get_bonus_instance_id = profile("get_bonus_instance_id", get_bonus_instance_id)
	table.remove(arg, 1)
end

local function convertItem(id)
	local info = xml.parse(getiteminfo(id)):get_first_child("itemInfo"):get_first_child("item")
	local tooltip = xml.parse(getitemtooltip(id)):get_first_child("itemTooltips"):get_first_child("itemTooltip")
	
	if not info or not tooltip then
		print("Missing info for item, skipping...", id)
		return
	end

	local item_id = id
	-- information(s) from item-info:
	assert (item_id == tonumber(info:get_attribute("id")))
	local item_name = info:get_attribute("name")
	local item_cost = get_item_cost(info)
	local item_level = tonumber(info:get_attribute("level"))
	local item_icon = info:get_attribute("icon")
	local item_quality = tonumber(info:get_attribute("quality"))

	-- information(s) from item-tooltip:
	assert (item_id == tonumber(tooltip:get_first_child("id"):get_content()))
	assert (item_name == tooltip:get_first_child("name"):get_content())
--~ 		assert (item_icon == tooltip:get_first_child("icon"):get_content())
	assert (item_quality == tonumber(tooltip:get_first_child("overallQualityId"):get_content()))
	local item_bonding = tonumber(tooltip:get_first_child("bonding"):get_content())
	local item_class_id = tonumber(tooltip:get_first_child("classId"):get_content())
	local item_type_id, item_subclass_id = get_item_type(tooltip)
	local item_maxcount = get_item_maxcount(tooltip)
	local item_conjured = tooltip:get_first_child("conjured") and 1 or 0
	local item_zonebound = get_item_bind_id(tooltip:get_first_child("zoneBound"))
	local item_instancebound = get_item_bind_id(tooltip:get_first_child("instanceBound"))
	local item_speed = get_item_damage_info(tooltip:get_first_child("damageData"), item_id)
	local item_durability = get_item_durability(tooltip:get_first_child("durability"))
	local item_socketbonus = get_item_socketbonus(tooltip:get_first_child("socketData"), item_id)
	local item_reqlevel = get_item_requirements(tooltip, item_id)
	
	apply_stmt([[
INSERT INTO items 
(id, name, cost, level, icon, quality, bonding, class_id, type_id,
 subclass_id, maxcount, conjured, zonebound_id, instancebound_id,
 speed, durability, socketmatchbonus_id, reqlevel)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]],
		item_id, item_name, item_cost, item_level, item_icon, item_quality,
		item_bonding, item_class_id, item_type_id, item_subclass_id,
		item_maxcount, item_conjured, item_zonebound, item_instancebound,
		item_speed, item_durability, item_socketbonus, item_reqlevel)
	
	handle_item_set_info(tooltip:get_first_child("setData"), item_id)
	handle_bonus_info(tooltip, info, item_id)
	handle_quest_info(info, item_id)
	handle_creature_info(info, item_id)
	handle_container_info(info, item_id)
	handle_disenchant_info(info, item_id)
	handle_craft_info(info, item_id)
	handle_currency_info(info, item_id)
end

local in_cache
local function get_cache_boundaries(min, max)
	local id_min, id_max
	in_cache = {}
	for id in cache_db:urows("SELECT DISTINCT id FROM cache") do
		id = tonumber(id)
		in_cache[id] = true
		if not id_min or id_min > id then
			id_min = id
		end
		if not id_max or id_max < id then
			id_max = id
		end
	end
	if min then id_min = math.max(id_min, min) end
	if max then id_max = math.min(id_max, max) end
	local id_count = 0
	for i = id_min, id_max do
		if in_cache[i] then
			id_count = id_count + 1
		end
	end
	print(string.format("In cache : %d items from %d to %d", id_count, id_min, id_max))
	return id_min, id_max, id_count
end

local function main()
	local min, max, total = get_cache_boundaries(tonumber(arg[1]), tonumber(arg[2]))
	local count, last_time, percent = 0, os.clock(), 0
	for id = min, max do
		if in_cache[id] then
			count = count + 1
			local p = math.floor(count / total * 100)
			if p ~= percent then
				local t = os.clock()
				print(p, id, t - last_time) io.flush()
				percent = p
				last_time = t
			end
			items_db:execute("BEGIN EXCLUSIVE TRANSACTION")
			local status, err = pcall(convertItem, id)
			if not status then
				print("ERROR", id, err)
			end
			items_db:execute("END TRANSACTION")
		end
	end
end

main()
report()
